#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>  /* See NOTES */
#include <netinet/ip.h> /* superset of previous */
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <pthread.h>
#include "cJSON.h"
#include "shmem.h"
#include "msg_queue_peer.h"

struct msg
{
    long msg_type; // 消息的类型
    int key;
    char val[256];
};

union val_t
{
    int b_val;   // bool类型存储空间
    int i_val;   // 整形值存储空间
    float f_val; // 浮点值存储空间
};

struct std_node
{
    int key;             // 唯一键值
    int type;            // 数据点类型
    int dev_type;        // 数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; // 变化上报后需要更新旧值
    union val_t new_val; // 从共享内存取出最新数据，放到new_val中
    int ret;             // 默认为-1，采集成功后设置为0，采集失败再置-1
};

#define N 2048
char buf[N] = {};   // 客户端
char buf_p[N] = {}; // 点表
struct std_node *st;
int clifd; //单片机连接的套接字
union val_t vt;

void *handler_read(void *arg)
{
    struct msg m;

    //以下是调试，运行时需注释
    // int v;
    // scanf("%d", &v);

    // m.msg_type = 0;
    // m.key = 303;
    // sprintf(m.val,"%d",v);

    //以下是从消息队列读取命令
    if (msg_queue_recv("msg", &m, sizeof(struct msg), 1, 0) < 0) // 需再改
    {
        printf("msg recv err\n");
        return NULL;
    }

    // 将读取的命令读取后序列化发送给单片机进行控制
    cJSON *root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "key", cJSON_CreateNumber(m.key));
    cJSON_AddItemToObject(root, "val", cJSON_CreateNumber(atoi(m.val)));
    char *p = cJSON_Print(root);

    send(clifd, p, 128, 0);

    free(p);
    cJSON_Delete(root);
}

int main(int argc, char const *argv[])
{
    pthread_t tid;

    //获取共享内存创建的node.json文件属性
    while (1)
    {
        struct stat sta;
        if (stat("/mnt/config/node.json", &sta) < 0)
        {
            perror("stat err\n");
            continue;
        }
        if (sta.st_size < 100)
            continue;
        else
            break;
    }

    //打开点表的文件
    FILE *fp = fopen("/mnt/config/node.json", "r");
    if (NULL == fp)
    {
        perror("fopen err\n");
        return -1;
    }
    fread(buf_p, 1, N, fp);

    // 获取单片机对应的key值
    cJSON *root = cJSON_Parse(buf_p);
    cJSON *stm = cJSON_GetObjectItem(root, "stm32");
    cJSON *data = cJSON_GetObjectItem(stm, "data");

    cJSON *item_0 = cJSON_GetArrayItem(data, 0);
    cJSON *t = cJSON_GetObjectItem(item_0, "key");
    int tem = t->valueint;

    cJSON *item_1 = cJSON_GetArrayItem(data, 1);
    cJSON *h = cJSON_GetObjectItem(item_1, "key");
    int hum = h->valueint;

    cJSON *item_2 = cJSON_GetArrayItem(data, 2);
    cJSON *l = cJSON_GetObjectItem(item_2, "key");
    int lig = l->valueint;
    cJSON_Delete(root);

    //创建tcp连接单片机的wifi模块
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0)
    {
        perror("sock err\n");
        return -1;
    }

    //填充网络属性
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8888); //连接的端口号是8888
    saddr.sin_addr.s_addr = INADDR_ANY;

    //绑定
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err\n");
        return -1;
    }

    //监听
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err\n");
        return -1;
    }

    int maxfd = sockfd;
    fd_set rdfs, tmpfs;
    FD_ZERO(&rdfs);
    FD_ZERO(&tmpfs);
    FD_SET(sockfd, &rdfs);

    //创建表，只关心需要的套接字
    while (1)
    {
        tmpfs = rdfs;
        select(maxfd + 1, &tmpfs, NULL, NULL, NULL);

        for (size_t i = 0; i < maxfd + 1; i++)
        {
            if (FD_ISSET(i, &tmpfs))
            {
                if (i == sockfd)
                {
                    // 接收新用户连接
                    clifd = accept(sockfd, NULL, NULL);
                    printf("new connect come\n");
                    printf("client fd:%d\n", clifd);

                    FD_SET(clifd, &rdfs);
                    if (clifd > maxfd)
                    {
                        maxfd = clifd;
                    }
                }
                else // 客户端有数据
                {
                    memset(buf, 0, N);
                    int ret = recv(i, buf, N, 0);
                    if (ret < 0)
                    {
                        perror("recv err\n");
                        return -1;
                    }
                    else if (ret == 0)
                    {
                        //表示读取完成，关闭连接
                        FD_CLR(i, &rdfs);
                        close(i);
                        printf("new client discon\n");
                        break;
                    }
                    else if (ret > 0)
                    {
                        //表示有数据
                        //将读取到的单片机的数据反序列化
                        cJSON *root = cJSON_Parse(buf);
                        cJSON *data = cJSON_GetObjectItem(root, "data");

                        // 温度 double
                        cJSON *tem_1 = cJSON_GetArrayItem(data, 0);
                        cJSON *tem_2 = cJSON_GetObjectItem(tem_1, "val");
                        double t = tem_2->valuedouble;

                        // 湿度 double
                        cJSON *hum_1 = cJSON_GetArrayItem(data, 1);
                        cJSON *hum_2 = cJSON_GetObjectItem(hum_1, "val");
                        double hu = hum_2->valuedouble;

                        // 风机 bool
                        cJSON *wind_1 = cJSON_GetArrayItem(data, 2);
                        cJSON *wind_2 = cJSON_GetObjectItem(wind_1, "val");
                        int w = wind_2->valueint;
                        cJSON_Delete(root);

                        //在终端上打印单片机反序列化后发送的数据
                        printf("tem:%lf\n", t);
                        printf("hum:%lf\n", hu);
                        printf("wind:%d\n", w);
                        printf("------------------------------\n");

                        // //制冷 bool
                        // cJSON *cold = cJSON_GetArrayItem(data,3);
                        // cJSON *cold_1 = cJSON_GetObjectItem(cold,"val");
                        // int c = cold_1->valueint;

                        // //制热 bool
                        // cJSON *hot = cJSON_GetArrayItem(data,4);
                        // cJSON *hot_1 = cJSON_GetObjectItem(hot,"val");
                        // int h = hot_1->valueint;


                        //打开共享内存
                        struct shm_param para;
                        if (shm_init(&para, "shm", 256) < 0)
                        {
                            printf("shm init err\n");
                            return -1;
                        }

                        //创建一个头
                        int *m = shm_getaddr(&para);
                        if (m == NULL)
                        {
                            printf("get addr err\n");
                            return -1;
                        }
                        st = (struct std_node *)(m + 1);
                        for (int i = 0; i < *m; i++)
                        {
                            //根据key值，修改对应的数据
                            if (st->key == tem)
                                st->new_val.f_val = t;
                            else if (st->key == hum)
                                st->new_val.f_val = hu;
                            else if (st->key == lig)
                                st->new_val.b_val = w;

                            st += 1;
                        }
                    }
                }
            }
        }

        //建立线程处理上方发送的控制指令
        if (pthread_create(&tid, NULL, handler_read, NULL) < 0)
        {
            perror("pthread create err\n");
            return -1;
        }
    }
    fclose(fp);
    pthread_detach(tid);
    close(clifd);
    close(sockfd);

    return 0;
}
